package owg.util.editor2d.operations;

import owg.util.editor2d.Editor2D;
import owg.util.editor2d.StateInfo;
/**Represents half of a transaction. A complete transaction consists of a forward and a backward operation.
 * Note that selection data is managed by {@link StateInfo}, not by {@link Operation}, and it is recorded separately.
 * If only selection data is modified, then {@link #NO_OPERATION} can be used in both directions.*/
public abstract class Operation
	{
	public static final Operation NO_OPERATION = new Nop();
	
	public abstract void apply(Editor2D w) throws OperationFailedException;
	
//	public static class LinkOperation extends Operation
//		{
//		public static class LinkData implements Serializable
//			{
//			private static final long serialVersionUID = 2988539063989825530L;
//			
//			public int fromIndexAbsolute;
//			public final int[] fromIndexLinksAbsolute;
//			
//			/**Only used by kryo*/
//			@SuppressWarnings("unused")
//			private LinkData()
//				{fromIndexAbsolute=0; fromIndexLinksAbsolute=null;}
//			
//			public LinkData(Linkable from) 
//				{
//				ArrayList<Pair<Placeable2D, ConstructorParams>> ps = Editor2D.me.getSelectableObjects();
//				Linkable[] fps = from.getLinks();
//				if(fps != null)
//					fromIndexLinksAbsolute = new int[fps.length];
//				else
//					fromIndexLinksAbsolute = null;
//				int fi = -1;
//				for(int i = 0; i<ps.size(); i++)
//					{
//					Placeable2D p = ps.get(i).a;
//					if(p == from)
//						fi = i;
//
//					if(fps != null)
//						{
//						for(int j = 0; j<fps.length; j++)
//							{
//							if(p == fps[j])
//								fromIndexLinksAbsolute[j] = i;
//							}
//						}
//					}
//				assert fi != -1;
//
//				if(fps != null)
//					assert Editor2D.me.noDuplicates(Arrays.asList(fromIndexLinksAbsolute));
//				fromIndexAbsolute = fi;
//				}
//			public void offset(int offset)
//				{
//				fromIndexAbsolute += offset;
//				for(int i = 0; i<fromIndexLinksAbsolute.length; i++)
//					fromIndexLinksAbsolute[i] += offset;
//				}
//			
//			public void apply(Editor2D w)
//				{
//				ArrayList<Pair<Placeable2D, ConstructorParams>> ps = Editor2D.me.getSelectableObjects();
//				
//				Linkable from = (Linkable)ps.get(fromIndexAbsolute).a;
//				
//				if(fromIndexLinksAbsolute != null)
//					{
//					Linkable[] fps = new Linkable[fromIndexLinksAbsolute.length];
//					
//					for(int i = 0; i<fps.length; i++)
//						fps[i] = (Linkable) ps.get(fromIndexLinksAbsolute[i]).a;
//					
//					from.setLinks(fps);
//					}
//				from.transformationChanged(Placeable2D.CHANGE_LINKED_OBJECTS);
//				}
//			}
//		public final LinkData[] data;
//		/**Construct a link operation from the given link data as it is.*/
//		public LinkOperation(LinkData[] data)
//			{
//			this.data = data;
//			}
//		/**Constructs a link operation from the current link state of the two objects. This will log the state of both objects.*/
//		public LinkOperation(Linkable from, Linkable to)
//			{
//			data = new LinkData[]{new LinkData(from), new LinkData(to)};
//			}
//
//		@Override
//		public void apply(Editor2D w) throws OperationFailedException
//			{
//			for(LinkData d:data)
//				d.apply(w);
//			}
//		}
//	
//	public static class CustomOperation extends Operation
//		{
//		final Integer[][] linksIndicesAbsolute;
//		
//		final Integer[] objectIndicesAbsolute;
//		final Enum<?> property;
//		final Object[] values;
//		/**Creates an operation that will set the indicated property on all selected objects of the same type as the pivot
//		 * to the values that they have right now.<br/>
//		 * <br/>
//		 * This constructor should be used to specify an inverse operation.<br/>
//		 * <br/>
//		 * If the pivot is a linkable, then this constructor will store the list of linked instances for each object, 
//		 * so that it may be restored in the case that the forward operation, which sets the property 
//		 * to a specified value, forces links to be broken. 
//		 * Links will be individually readded iff they have been broken.*/
//		public CustomOperation(Customizable pivot, Enum<?> property)
//			{
//			ArrayList<Pair<Placeable2D, ConstructorParams>> ps = Editor2D.me.getSelectableObjects();
//			ArrayList<Integer> inds = new ArrayList<Integer>();
//			ArrayList<Object> values = new ArrayList<Object>();
//			for(int i = 0; i<ps.size(); i++)
//				{
//				if(pivot.getClass().equals(ps.get(i).a.getClass()) && Editor2D.me.isSelected(ps.get(i).a))
//					{
//					inds.add(i);
//					values.add(((Customizable)ps.get(i).a).getParameterValue(property));
//					}
//				}
//			objectIndicesAbsolute = new Integer[inds.size()];
//			inds.toArray(objectIndicesAbsolute);
//			this.values = new Object[inds.size()];
//			values.toArray(this.values);
//			this.property = property;
//			
//
//			if(pivot instanceof Linkable)
//				{
//				//Now, for each object, store the list of links as indices in the worldObjects list
//				linksIndicesAbsolute = new Integer[objectIndicesAbsolute.length][];
//				for(int k = 0; k<objectIndicesAbsolute.length; k++)
//					{
//					//Since each object is the same type, the cast is verified.
//					Linkable current = (Linkable)ps.get(objectIndicesAbsolute[k]).a;
//					Linkable[] links = current.getLinks();
//					linksIndicesAbsolute[k] = new Integer[links.length];
//					for(int i = 0; i<links.length; i++)
//						{
//						search:
//						for(int j = 0; j<ps.size(); j++)
//							{
//							if(ps.get(j).a == links[i])
//								{
//								linksIndicesAbsolute[k][i] = j;
//								break search;
//								}
//							}
//						}
//					}
//				}
//			else
//				linksIndicesAbsolute = null;
//			}
//		/**Creates operation that will set the indicated property on all 
//		 * selected objects of the same type as the pivot to the specified value.
//		 * This constructor should be used to specify a forward operation. 
//		 * This constructor does not store the list of linked instances, 
//		 * since custom operations are not allowed to establish links.
//		 * However, the operation may cause links to be broken.*/
//		public CustomOperation(Customizable pivot, Enum<?> params, Object value)
//			{
//			ArrayList<Pair<Placeable2D, ConstructorParams>> ps = Editor2D.me.getSelectableObjects();
//			ArrayList<Integer> inds = new ArrayList<Integer>();
//			for(int i = 0; i<ps.size(); i++)
//				{
//				if(pivot.getClass().equals(ps.get(i).a.getClass()) && Editor2D.me.isSelected(ps.get(i).a))
//					inds.add(i);
//				}
//			objectIndicesAbsolute = new Integer[inds.size()];
//			inds.toArray(objectIndicesAbsolute);
//			this.property = params;
//			values = new Object[inds.size()];
//			Arrays.fill(values, value);
//			linksIndicesAbsolute = null;
//			}
//
//		@Override
//		public void apply(Editor2D w) throws OperationFailedException
//			{
//			ArrayList<Pair<Placeable2D, ConstructorParams>> ps = w.getSelectableObjects();
//			for(int i = 0; i<objectIndicesAbsolute.length; i++)
//				{
//				Placeable2D p = ps.get(objectIndicesAbsolute[i]).a;
//				((Customizable)p).setParameterValue(property, values[i]);
//				p.transformationChanged(Placeable2D.CHANGE_CUSTOM_PARAMETER);
//				
//				if(p == w.getPivotObject())
//					w.pivotParametersChanged(Placeable2D.CHANGE_CUSTOM_PARAMETER);
//				
//				if(linksIndicesAbsolute != null)
//					{
//					boolean ch = false;
//					if(!(p instanceof Linkable))
//						new Throwable("Error: "+p+" should be a Linkable(pivot), but isn't! "+this).printStackTrace();
//					else
//						{
//						Linkable[] currentLinks = ((Linkable)p).getLinks(); 
//						
//						for(int srcIndex = 0; srcIndex<linksIndicesAbsolute[i].length; srcIndex++)
//							{
//							Placeable2D keyCandidate = ps.get(linksIndicesAbsolute[i][srcIndex]).a;
//							if(!(keyCandidate instanceof Linkable))
//								System.err.println("Warning: "+keyCandidate+" should be a Linkable(target), but isn't!"+this);
//							else
//								{
//								Linkable key = (Linkable)keyCandidate;
//								if(!Arrays.asList(currentLinks).contains(key))
//									{
//									System.out.println("Readding broken link from customizable: "+p+" to "+key);
//									((Linkable)p).addLink(key);
//									ch = true;
//									}
//								}
//							}
//						}
//					if(ch)
//						((Linkable)p).transformationChanged(Placeable2D.CHANGE_LINKED_OBJECTS);
//					//Oh dear.
//					}
//				}
//			}
//		}
	}
